#include<iostream>
#include<string>
#include"mongoose.h"
#include"util.hpp"
#include"db.hpp"
#include"session.hpp"
using namespace std;

namespace im{
  class Server{
    private:
      struct mg_mgr _mgr;
      string _rootdir="./wwwroot";
      MsgTable *_msg_table;
      UserTable *_user_table;
      SessionManager *_ss_manager;
    private:
      static bool user_add(struct mg_connection *c,
      struct mg_http_message *hm,Server *srv)
      {
        string req_body(hm->body.ptr,hm->body.len);//获取信息
        Json::Value user_json;
        Json::Value resp_json;
        bool ret=JsonUtil::unserialize(req_body,&user_json);//反序列化
        if(ret==false)
        {
          resp_json["result"]=false;
          resp_json["reason"]="用户信息解析失败";
          string body;
          JsonUtil::serialize(resp_json,&body);
          mg_http_reply(c,400,NULL,"%s",body.c_str());//反序列化失败
          return false;
        }
        UserTable *table=srv->get_user_table();
        ret=table->insert(user_json);
        if(ret==false)
        {
          resp_json["result"]=false;
          resp_json["reason"]="向数据库插入数据失败";
          string body;
          JsonUtil::serialize(resp_json,&body);
          mg_http_reply(c,500,NULL,"%s",body.c_str());//回复插入数据失败
          return false;
        }
        resp_json["result"]=true;
        resp_json["reason"]="新增用户成功";
        string body;
        JsonUtil::serialize(resp_json,&body);
        mg_http_reply(c,200,NULL,"%s",body.c_str());//新增成功，响应成功
      }

      static bool user_login(struct mg_connection *c,
          struct mg_http_message *hm,Server *srv)
      {
        //从请求正文中获取用户名，密码信息
        string req_body(hm->body.ptr,hm->body.len);//获取信息
        //进行json反序列化
        Json::Value user_json;
        Json::Value resp_json;
        bool ret=JsonUtil::unserialize(req_body,&user_json);//反序列化
        if(ret==false)
        {
          resp_json["result"]=false;
          resp_json["reason"]="用户信息解析失败";
          string body;
          JsonUtil::serialize(resp_json,&body);
          mg_http_reply(c,400,NULL,"%s",body.c_str());//反序列化失败
          return false;
        }
        //在数据库中进行验证
        UserTable *table=srv->get_user_table();
        ret=table->check_login(user_json);
        if(ret==false)
        {
          resp_json["result"]=false;
          resp_json["reason"]="用户名或密码错误";
          string body;
          JsonUtil::serialize(resp_json,&body);
          mg_http_reply(c,401,NULL,"%s",body.c_str());//回复登录失败
          return false;
        }
        //为客户端新建会话
        SessionManager *ssm=srv->get_ss_manager();
        uint64_t ssid=ssm->insert(user_json);
        string cookie="set-Cookie:SSID="+to_string(ssid)+"\r\n";//设置头部字段

        //返回200
        resp_json["result"]=true;
        resp_json["reason"]="用户登录成功";
        string body;
        JsonUtil::serialize(resp_json,&body);
        mg_http_reply(c,200,cookie.c_str(),"%s",body.c_str());//登录成功
      }
      static bool get_history_msg(struct mg_connection *c,//获取历史信息
          struct mg_http_message *hm,Server *srv) 
          {
            struct mg_str *cookie=mg_http_get_header(hm,"Cookie");//获取cookie字段
            if(nullptr==cookie)//没有cookie直接进入聊天界面
            {
              cout<<"没有获取到Cookie"<<endl;
              mg_http_reply(c,401,"","%s","401 Unauthorized");
              return false;
            }
            string cookie_str(cookie->ptr,cookie->len);
            size_t pos=cookie_str.find("=");
            string ssid_str=cookie_str.substr(pos+1);//截取ssid
            uint64_t ssid=stol(ssid_str);
            SessionManager *ssm=srv->get_ss_manager();
            if(ssm->exists(ssid)==false)//没有登陆过
            {
              cout<<"没有对应的session!"<<endl;
              mg_http_reply(c,401,"","%s","401 Unauthorized");
              return false;
            }
            MsgTable *table=srv->get_msg_table();
            Json::Value msgs;
            bool ret=table->select_part(300000,&msgs);
            if(ret==false)
            {
              Json::Value resp_json;
              resp_json["result"]=false;
              resp_json["reason"]="获取历史聊天消息失败";
              string body;
              JsonUtil::serialize(resp_json,&body);
              mg_http_reply(c,500,NULL,"%s",body.c_str());//获取历史聊天消息失败
            }
            string body;
            JsonUtil::serialize(msgs,&body);
            Json::Value user;
            ssm->get_session_user(ssid,&user);
            ssm->remove(ssid);
            ssid=ssm->insert(user);
            string header="Content-Type:application/json\r\n";
            header+="Set-Cookie:SSID="+to_string(ssid)+"\r\n";
            mg_http_reply(c,200,header.c_str(),"%s",body.c_str());//获取历史消息成功
          }
          //广播接口
          static void broadcast_msg(struct mg_mgr *mgr,const string &msg)
          {
            struct mg_connection *conn=mgr->conns;
            for(;conn!=nullptr;conn=conn->next)
            {
              if(conn->is_websocket)//只给在线的人广播
              {
                mg_ws_send(conn,msg.c_str(),msg.size(),WEBSOCKET_OP_TEXT);//发送消息
              }
            }
          }
          static void handler(struct mg_connection *c,int ev,void *ev_data,void *fn_data)
          {
            Server *srv=(Server*)fn_data;
            if(ev==MG_EV_HTTP_MSG)//收到一个http请求
          {
            struct mg_http_message *hm=(struct mg_http_message*) ev_data;
            string method(hm->method.ptr,hm->method.len);//资源方法
            string uri(hm->uri.ptr,hm->uri.len);//请求路径
            cout<<"------"<<method<<"\t"<<uri<<endl;
            if(method=="POST"&&uri=="/user")//新增用户信息
            {
              user_add(c,hm,srv);
            }
            else if(method=="POST"&&uri=="/login")//登录验证
            {
              user_login(c,hm,srv); 
            }
            else if(method=="PUT"&&uri=="/user/passwd")//修改密码
            {}
            else if(method=="PUT"&&uri=="/user/nickname")//修改昵称
            {}
            else if(method=="DELETE"&&uri=="/user")//删除用户
            {}
            else if(method=="GET"&&uri=="/user")//获取用户信息
            {
              struct mg_str *cookie=mg_http_get_header(hm,"Cookie");//获取cookie字段
              if(nullptr==cookie)//没有cookie直接进入聊天界面
              {
                cout<<"没有获取到Cookie"<<endl;
                mg_http_reply(c,401,"","%s","401 Unauthorized");
                return ;
              }
              string cookie_str(cookie->ptr,cookie->len);
              size_t pos=cookie_str.find("=");
              string ssid_str=cookie_str.substr(pos+1);//截取ssid
              uint64_t ssid=stol(ssid_str);
              SessionManager *ssm=srv->get_ss_manager();
              if(ssm->exists(ssid)==false)//没有登陆过
              {
                cout<<"没有对应的session!"<<endl;
                mg_http_reply(c,401,"","%s","401 Unauthorized");
                return;
              }
              Json::Value user;
              ssm->get_session_user(ssid,&user);

              UserTable *table=srv->get_user_table();
              Json::Value new_user;
              table->select_by_name(user["username"].asString(),&new_user);
              string user_str;
              JsonUtil::serialize(new_user,&user_str);
 
              /*ssm->remove(ssid);//移除原先的session 
              ssid=ssm->insert(new_user);//新建
              string header="Content-Type:application/json\r\n";
              header+="Set-Cookie:SSID="+to_string(ssid)+"\r\n";*/
              mg_http_reply(c,200,"Content-Type:application/json\r\n","%s",user_str.c_str());
               
            }
            else if(method=="GET"&&uri=="/message")//获取历史聊天消息
            {
              get_history_msg(c,hm,srv);
            }
            else if(method=="GET"&&uri=="/ws")//协议切换请求--切换到websocket协议
            {
              struct mg_str *cookie=mg_http_get_header(hm,"Cookie");//获取cookie字段
              if(nullptr==cookie)//没有cookie直接进入聊天界面
              {
                mg_http_reply(c,401,"","%s","401 Unauthorized");
                return ;
              }
              string cookie_str(cookie->ptr,cookie->len);

              size_t pos=cookie_str.find("=");
              string ssid_str=cookie_str.substr(pos+1);//截取ssid
              uint64_t ssid=stol(ssid_str);
              SessionManager *ssm=srv->get_ss_manager();
              if(ssm->exists(ssid)==false)//没有登陆过
              {
                mg_http_reply(c,401,"","%s","401 Unauthorized");
                return;
              }
              //cout<<"--------["<<cookie_str<<"]"<<endl;
              mg_ws_upgrade(c,hm,NULL);//协议切换完成
              ssm->remove(ssid);
            }
            else {
              struct mg_http_serve_opts opts={.root_dir=srv->root_dir().c_str()};
              mg_http_serve_dir(c,hm,&opts);
            }
          }
          else if(ev==MG_EV_WS_OPEN)//WEBSOCKET 握手成功
          {} 
          else if(ev==MG_EV_WS_MSG)//数据通信
          {
            //广播一条消息
            struct mg_ws_message *wsmsg=(struct mg_ws_message*)ev_data;//收到消息
            string msg(wsmsg->data.ptr,wsmsg->data.len);//取出消息
            cout<<msg<<endl;
            broadcast_msg(srv->get_mger(),msg);//消息发送到所有登录的人
            Json::Value msg_json;//反序列化
            JsonUtil::unserialize(msg,&msg_json);
            srv->get_msg_table()->insert(msg_json);//数据插入表中
          }
          else if(ev==MG_EV_CLOSE)//连接断开
          {}
        }
    public:
      Server(const string &host,
           const string &user,
           const string &pass,
           const string &db,
           int port)
      {
        _user_table=new UserTable(host,user,pass,db,port);
        _msg_table=new MsgTable(host,user,pass,db,port);
        _ss_manager=new SessionManager();
        mg_mgr_init(&_mgr);
      }
      struct mg_mgr* get_mger(){return &_mgr;}
      const string &root_dir(){return _rootdir;}
      UserTable *get_user_table(){return _user_table;}
      MsgTable *get_msg_table(){return _msg_table;}
      SessionManager *get_ss_manager(){return _ss_manager;}

      void Start(int port)
      {
        string address="0.0.0.0:"+to_string(port);
        mg_http_listen(&_mgr,address.c_str(),handler,this);
        for(;;) mg_mgr_poll(&_mgr,1000);
      }
  };
}
